package juc;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.stream.IntStream;

/**
 * Lock及ReentrantLock测试
 * @author ben
 * @date 2021-09-08 09:58:26 CST
 */
public class TestLock {

	public static Consumer<Object> cs = System.out::println;
	
	/**
	 * 锁
	 */
	// 非公平锁：T1执行完，T2才执行
//	private static ReentrantLock glock = new ReentrantLock();
	// 公平锁：T1、T2交叉执行
	private static ReentrantLock glock = new ReentrantLock(true);
	
	/**
	 * 单线程测试可重入：HoldCount会变化
	 */
	private static ReentrantLock glock2 = new ReentrantLock();
	
	/**
	 * 共享变量
	 */
	private static Long accessCount = 0L;
	
	public static void main(String[] args) throws Exception {
//		test1();
		
//		test2();
		
//		deadLock();
		
		deadLock2();
	}
	
	/**
	 * 死锁测试-lock()和unlock()数量不匹配
	 * 使用 jps+jstack -l 可以看到死锁状态
	 * @author ben
	 * @date 2021-09-08 10:47:09 CST
	 * @throws InterruptedException
	 */
	private static void deadLock2() throws InterruptedException {
		cs.accept("程序开始...");
		
		DeadLockClass2 dlc = new DeadLockClass2();

		Thread t1 = new Thread(()->{
			cs.accept("t1开始");
			dlc.resource();
			cs.accept("t1结束");
		}, "T1");
		
		Thread t2 = new Thread(()->{
			cs.accept("t2开始");
			dlc.resource();
			cs.accept("t2结束");
		}, "T2");
		
		t1.start();
		t2.start();
		
		t1.join();
		t2.join();
		
		cs.accept("程序开始...");
	}
	
	/**
	 * 死锁测试
	 * 使用 jps+jstack -l 可以看到死锁状态
	 * @author ben
	 * @date 2021-09-08 10:19:26 CST
	 * @throws InterruptedException
	 */
	private static void deadLock() throws InterruptedException {
		cs.accept("程序开始...");
		
		DeadLockClass dlc = new DeadLockClass();
		
		Thread t1 = new Thread(()->{
			dlc.resource1();
		}, "T1");
		
		Thread t2 = new Thread(()->{
			dlc.resource2();
		}, "T2");
		
		t1.start();
		t2.start();

		// 检查DeadLockClass的两个锁的状态
		IntStream.range(0, 5).forEach(item->{
			cs.accept("\ncheckLockStatus i=" + item);
			DeadLockClass.checkLockStatus();
			try {
				TimeUnit.SECONDS.sleep(item+5);
			} catch (InterruptedException e) {
			}
		});
		
		t1.join();
		t2.start();
		cs.accept("程序结束...");
	}
	
	/**
	 * 测试可重入
	 * @author ben
	 * @date 2021-09-08 10:07:30 CST
	 * @throws InterruptedException
	 */
	private static void test2() throws InterruptedException {
		String tname= Thread.currentThread().getName();
		cs.accept(tname + ": holdCount=" + glock2.getHoldCount() + ", isHeld?=" + glock2.isHeldByCurrentThread());
		IntStream.range(0, 6).forEach(item->{
			glock2.lock();
			cs.accept(tname + ": 锁住，holdCount=" + glock2.getHoldCount() + ", isHeld?=" + glock2.isHeldByCurrentThread());
		});
		
		cs.accept("---");
		cs.accept(tname + ": holdCount=" + glock2.getHoldCount() + ", isHeld?=" + glock2.isHeldByCurrentThread());
		int holdCount = glock2.getHoldCount();
		for (int i=0; i<holdCount; i++) {
			glock2.unlock();
			cs.accept(tname + ": 释放，holdCount=" + glock2.getHoldCount() + ", isHeld?=" + glock2.isHeldByCurrentThread());
		}

		cs.accept("程序结束：");
		cs.accept(tname + ": holdCount=" + glock2.getHoldCount() + ", isHeld?=" + glock2.isHeldByCurrentThread());
	}
	
	/**
	 * 测试1：main、T1、T2 访问共享资源
	 * @author ben
	 * @date 2021-09-08 09:57:27 CST
	 * @throws InterruptedException
	 */
	private static void test1() throws InterruptedException {
		resource1(0);
		
		// 建立两个线程访问共享资源
		Thread t1 = new Thread(()->{
			IntStream.range(0, 10).forEach(item->{
				resource1(item);
			});
		}, "T1");
		
		Thread t2 = new Thread(()->{
			// 仅偶数参数
			IntStream.range(10, 20).filter(item->{return item%2 == 0;}).forEach(item->{
				resource1(item);
			});
		}, "T2");

		t2.start();
		t1.start();
		
		t1.join();
		t2.join();
		
		cs.accept("程序结束...accessCount=" + accessCount);
	}
	
	/**
	 * 共享资源
	 * @author ben
	 * @date 2021-09-08 09:53:39 CST
	 * @param i
	 */
	private static void resource1(int i) {
		String tname = Thread.currentThread().toString();
		glock.lock();
		cs.accept("lock: " + tname + ", i=" + i + ", hasQueuedThreads=" + glock.hasQueuedThreads());
		try {
			cs.accept(tname + ", holdCount=" + glock.getHoldCount() + ", qLen=" + glock.getQueueLength());
			accessCount++;
			try {
				TimeUnit.SECONDS.sleep(2);
			} catch (InterruptedException e) {
				cs.accept("interrupted: " + tname);
			}
		} finally {
			cs.accept("unlock: " + tname + ", accessCount=" + accessCount + "\n");
			glock.unlock();
		}
	}

}

/**
 * 死锁测试类
 * @author ben
 * @date 2021-09-08 10:20:09 CST
 */
class DeadLockClass {
	private static ReentrantLock lock1 = new ReentrantLock(true);
	private static ReentrantLock lock2 = new ReentrantLock(true);

	public static Consumer<Object> cs = System.out::println;
	
	/**
	 * 检查锁的状态
	 * @author ben
	 * @date 2021-09-08 10:32:59 CST
	 */
	public static void checkLockStatus() {
		cs.accept("lock1: " + lock1.getHoldCount() + "," + lock1.getQueueLength());
		cs.accept("lock2: " + lock2.getHoldCount() + "," + lock2.getQueueLength());
	}
	
	/**
	 * 使用lock1
	 * @author ben
	 * @date 2021-09-08 10:25:23 CST
	 */
	public void resource1() {
		String tname = Thread.currentThread().getName();
		lock1.lock();
		cs.accept(tname + ": lock1.lock");
		try {
			try {
				TimeUnit.SECONDS.sleep(2);
			} catch (InterruptedException e) {
			}
			
			// 调用资源2
			cs.accept(tname + ": call resource2");
			resource2();
		} finally {
			lock1.unlock();
			cs.accept(tname + "lock1.unlock");
		}
	}

	/**
	 * 使用lock2
	 * @author ben
	 * @date 2021-09-08 10:25:32 CST
	 */
	public void resource2() {
		String tname = Thread.currentThread().getName();
		lock2.lock();
		cs.accept(tname + ": lock2.lock");
		try {
			try {
				TimeUnit.SECONDS.sleep(2);
			} catch (InterruptedException e) {
			}
			
			// 调用资源1
			cs.accept(tname + ": call resource1");
			resource1();
		} finally {
			lock2.unlock();
			cs.accept(tname + ": lock2.unlock");
		}
	}
	
}

/**
 * 死锁测试2——lock数量和unlock数量不匹配
 * @author ben
 * @date 2021-09-08 10:48:36 CST
 */
class DeadLockClass2 {

	private static ReentrantLock glock = new ReentrantLock(true);

	public static Consumer<Object> cs = System.out::println;

	/**
	 * 检查锁的状态
	 * @author ben
	 * @date 2021-09-08 10:32:59 CST
	 */
	public static void checkLockStatus() {
		cs.accept("glock: HoldCount=" + glock.getHoldCount() + ", QueueLength=" + glock.getQueueLength());
	}

	/**
	 * 共享资源
	 * @author ben
	 * @date 2021-09-08 10:54:20 CST
	 */
	public void resource() {
		String tname = Thread.currentThread().getName();
		cs.accept(tname + ": HoldCount=" + glock.getHoldCount() + ", QueueLength=" + glock.getQueueLength());
		// 第一次lock
		glock.lock();
		cs.accept(tname + ": lock-1 HoldCount=" + glock.getHoldCount() + ", QueueLength=" + glock.getQueueLength());
		try {
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
			}
			
			// 第二次lock：错误！
			glock.lock();
			cs.accept(tname + ": lock-2 HoldCount=" + glock.getHoldCount() + ", QueueLength=" + glock.getQueueLength());
		} finally {
			glock.unlock();
			cs.accept(tname + ": unlock-1 HoldCount=" + glock.getHoldCount() + ", QueueLength=" + glock.getQueueLength());
		}
	}
	
}